//---------------------------------------------------------------------------------
// Copyright (c) 2008 VoiceLynx Project
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
//     1. The origin of this software must not be misrepresented; you must not
//        claim that you wrote the original software. If you use this software
//        in a product, an acknowledgment in the product documentation would be
//        appreciated but is not required.
//
//     2. Altered source versions must be plainly marked as such, and must not be
//        misrepresented as being the original software.
//
//     3. This notice may not be removed or altered from any source
//        distribution.
//---------------------------------------------------------------------------------

#include "VLPrerequisites.h"

#include "VLCircularBuffer.h"
#include "VLCodec.h"
#include "VLSound.h"

#include "fmod.hpp"
#pragma comment(lib, "fmodex_vc.lib")

#ifndef __VLSoundManager_h__
#define __VLSoundManager_h__


// A single sample (frame) of sound
typedef VLU16 SAMPLE;
#define VL_FMOD_SAMPLE_SIZE_IN_BYTES (sizeof(SAMPLE))
#define VL_FMOD_SAMPLE_MAX_LEVEL ((float) ((1 << (VL_FMOD_SAMPLE_SIZE_IN_BYTES * 8)) - 1))

// Reads and writes per second of the sound data (how many frames per second)
// Speex only supports these 3 values
#define VL_FMOD_SMAPLE_RATE  (8000)
//#define SAMPLE_RATE  (16000)
//#define SAMPLE_RATE  (32000)

// The frame is the "quanta" of data that gets sent at a time
// Always 0.064 seconds worth (8K=512, 16K=1024, 32K=2048)
#define VL_FMOD_SAMPLES_PER_FRAME  (2048 / (32000 / VL_FMOD_SMAPLE_RATE))   
#define VL_FMOD_FRAME_SIZE_IN_BYTES (VL_FMOD_SAMPLES_PER_FRAME * sizeof(SAMPLE))

// The FMOD sound will buffer 4 sounds
#define VL_FMOD_FRAMES_IN_SOUND 4

class VLFModSoundStreamPlayer;

//! Given to the recorder/player
struct VLFModSoundEnv
{
  virtual FMOD::System *getSystem() = 0;

  virtual void unregisterSoundStreamPlayer(VLFModSoundStreamPlayer *player) = 0;
};

//! Records using FMOD
class VLFModSoundRecorder
{
public:
  VLFModSoundRecorder(VLFModSoundEnv &soundEnv);
  virtual ~VLFModSoundRecorder();
  
  void  pullData(VLByteArrayPtrList &frames);
  float getActivityLevel();

  float getGain();
  void  setGain(float gainAmount);

private:
  VLFModSoundEnv &mSoundEnv;

  FMOD::Sound   *mSound;
  FMOD::Channel *mChannel;

  VLU32 mLastRecordPos;

  float mActivityLevel;
  float mGain;
};

//! Plays using FMOD
class VLFModSoundStreamPlayer : public VLISoundStreamPlayer
{
public:
  VLFModSoundStreamPlayer(VLFModSoundEnv &soundEnv);
  virtual ~VLFModSoundStreamPlayer();

  virtual void giveData(VLByteArrayPtr data);
  float getActivityLevel();

  float getGain();
  void  setGain(float gainAmount);

  void process();

private:

  void pushData(VLCircularBuffer &mIncommingBuffer);

  VLFModSoundEnv &mSoundEnv;

  VLCircularBuffer mIncomingSoundData;

  FMOD::Sound   *mSound;
  FMOD::Channel *mChannel;

  VLU32 mLastPlayPos;

  float mGain;
};

class VLFModStaticSound : public VLIStaticSound
{
public:
  VLFModStaticSound(VLFModSoundEnv &soundEnv, const VLString &source);
  virtual ~VLFModStaticSound();

  void play();
  //void stop();

private:
  VLFModSoundEnv  &mSoundEnv;
  FMOD::Sound     *mSound;
  //FMOD::Channel   *mChannel;
};

//! Manages the recorder and players
class VLFModSoundManager : public VLISoundManager,  public VLFModSoundEnv
{
public:
  VLFModSoundManager();
  virtual ~VLFModSoundManager();
  void initialize();

  virtual VLStaticSoundPtr createStaticSound(const VLString &source);
  virtual VLSoundStreamPlayerPtr createSoundStreamPlayer(VLDecoderPtr decoder);

  // TODO: make this more efficient
  virtual void clearCapturedSoundData() { mOutgoingBuffer.takeAll(); }

  // Called by core to get sound data to send over the wire
  VLByteArrayPtr takeCapturedSoundData();

  // call frequently
  void process();


  

  //void setCaptureVolume(float pct);
  //void setOutputVolume(float pct);

  //void setCaptureEnabled(bool enabled);

  float getTxVolume();

  // Called by internal functions to queue up data to be taken by core
  void sendData(VLByteArrayPtr data);

  // Get the FMOD system
  FMOD::System *getSystem() { return mSystem; }

  // Used internally to handle removing soundstream ptr from vector when
  // dtor of sound stream is called
  virtual void unregisterSoundStreamPlayer(VLFModSoundStreamPlayer *player);

private:  

  void updateRecording();
  void updateIncomming();

  FMOD::System *mSystem;

  VLFModSoundRecorder *mRecorder;

  typedef std::vector<VLFModSoundStreamPlayer *> VLFModSoundStreamPlayerList;
  VLFModSoundStreamPlayerList mStreamPlayers;

  // These are touched by the outside world and populated by calling process() on the manager
  VLCircularBuffer mOutgoingBuffer;

  bool mEnableMic;
};


#endif